﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Controls;
using System.Windows;
using System.ComponentModel;
using System.Windows.Controls.Primitives;
using System.Windows.Input;

namespace Microsoft.Forums.ForumsClient.Views
{
    /// <summary>
    /// A Tree view with columns
    /// </summary>
    public class ColumnsTreeView : TreeView
    {

        readonly double MinimumColumnWidth = 35;

        /// <summary>
        /// The columns in the columns tree view
        /// </summary>
        private GridViewColumnCollection columns;

        /// <summary>
        /// Creates or identifies the element that is used to display the given item.
        /// </summary>
        /// <returns>The element that is used to display the given item.</returns>
        protected override DependencyObject GetContainerForItemOverride()
        {

            return new ColumnsTreeViewItem();
        }

        /// <summary>
        /// Determines whether the specified item is its own container or can be its own container.
        /// </summary>
        /// <param name="item">The object to evaluate.</param>
        /// <returns>true if item is a ColumnsTreeViewItem; otherwise, false.</returns>
        protected override bool IsItemItsOwnContainerOverride(object item)
        {
            return item is ColumnsTreeViewItem;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="ColumnsTreeView"/> class.
        /// </summary>
        public ColumnsTreeView()
        {
            this.AddHandler(Thumb.DragCompletedEvent, new DragCompletedEventHandler(Thumb_DragCompleted), true);
        }


        /// <summary>
        /// Invoked whenever the effective value of any dependency property on this <see cref="T:System.Windows.FrameworkElement"/> has been updated. The specific dependency property that changed is reported in the arguments parameter. Overrides <see cref="M:System.Windows.DependencyObject.OnPropertyChanged(System.Windows.DependencyPropertyChangedEventArgs)"/>.
        /// </summary>
        /// <param name="e">The event data that describes the property that changed, as well as old and new values.</param>
        protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e)
        {
            base.OnPropertyChanged(e);

            if (e.Property.Name.Equals("ActualWidth", StringComparison.Ordinal))
            {
                if (e.OldValue != e.NewValue)
                {
                    FitColumnsToTreeViewWidth();
                }
            }

        }

        #region Public Properties

        /// <summary> List of columns in the tree view</summary>
        public GridViewColumnCollection Columns
        {
            get
            {
                if (columns == null)
                {
                    columns = new GridViewColumnCollection();
                }

                return columns;
            }
        }


        /// <summary>
        /// Handles the DragCompleted event of the Thumb control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Windows.Controls.Primitives.DragCompletedEventArgs"/> instance containing the event data.</param>
        private void Thumb_DragCompleted(object sender, DragCompletedEventArgs e)
        {
            if (e.HorizontalChange != 0)
            {

                if (this.Columns.Count <= 1) { return; }

                double deltaWidth = e.HorizontalChange;

                Thumb thumb = e.OriginalSource as Thumb;
                GridViewColumnHeader header = thumb.TemplatedParent as GridViewColumnHeader;
                if (header != null)
                {

                    int changedColumnIndex = this.Columns.IndexOf(header.Column);

                    // Don't allow resizing the last column
                    if (changedColumnIndex == (this.Columns.Count - 1))
                    {
                        header.Column.Width = header.Column.ActualWidth - deltaWidth;
                        return;
                    }

                    // If the resized column is shrunk past the minimum
                    if (header.Column.ActualWidth < MinimumColumnWidth)
                    {
                        deltaWidth += (MinimumColumnWidth - header.Column.ActualWidth);
                        header.Column.Width = MinimumColumnWidth;
                    }


                    double numberOfColumnsToResize = this.Columns.Count - 1 - changedColumnIndex;

                    int minReachedCount = 0;
                    double deltaPart = deltaWidth / numberOfColumnsToResize;
                    foreach (GridViewColumn gridColumn in this.Columns.Skip(changedColumnIndex + 1))
                    {
                        if (gridColumn != header.Column)
                        {
                            double newWidth = gridColumn.ActualWidth - deltaPart;
                            if (newWidth < MinimumColumnWidth)
                            {
                                double amountUsed = gridColumn.ActualWidth - MinimumColumnWidth;
                                deltaWidth -= amountUsed;

                                minReachedCount++;
                                double columnsLeft = numberOfColumnsToResize - minReachedCount;
                                if (columnsLeft > 0)
                                {
                                    deltaPart = deltaWidth / columnsLeft;
                                }

                                gridColumn.Width = MinimumColumnWidth;

                            }
                            else
                            {
                                gridColumn.Width = newWidth;
                                deltaWidth -= deltaPart;
                            }
                        }
                    }

                    if (deltaWidth > 0)
                    {
                        header.Column.Width = header.Column.ActualWidth - deltaWidth;
                    }

                    // After the above algorithm runs we then do some touching up
                    // by refiting to the width of the control
                    FitColumnsToTreeViewWidth();
                }
            }
        }

        /// <summary>
        /// Fits the width of the columns to tree views width.
        /// </summary>
        private void FitColumnsToTreeViewWidth()
        {
            double totalWidth = 0;
            double excessWidth = 0;

            foreach (GridViewColumn gc in this.Columns)
            {
                totalWidth += gc.ActualWidth;
            }

            excessWidth = this.ActualWidth - totalWidth;

            double deltaPerColumn = excessWidth / (double)this.Columns.Count;

            foreach (GridViewColumn gc in this.Columns)
            {
                double newWidth = gc.ActualWidth + deltaPerColumn;
                gc.Width = newWidth < MinimumColumnWidth ? MinimumColumnWidth : newWidth;

            }


        }

        #endregion
    }
}
